feat: integrate Story (1514) as second-class Relay chain#11936
feat: integrate Story (1514) as second-class Relay chain#11936gomesalexandre merged 7 commits intodevelopfrom
Conversation
📝 WalkthroughWalkthroughThis PR adds comprehensive support for the Story blockchain (chainId: eip155:1514) across the ShapeShift application. Changes include CAIP constants, EVM chain adapters, wallet capability flags, environment configuration, feature flags, asset generation scripts, CSP headers, blockchain clients, state management, plugin registration, and UI updates for a second-class EVM chain. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fix duplicate imports across ~15 files from merge conflict resolution - fix merged lines in .env/.env.development (split node URLs/feature flags) - fix no-dupe-keys in baseAssets.ts (sonic, unichain, BOB, mode icons) - fix wallet.ts broken supportsBlast/supportsWorldChain/supportsHemi functions - fix coingecko index.test.ts duplicate ethOnBob/ethOnZkSyncEra entries - fix market service test destructuring (27 → 17 unique entries) - fix generateRelatedAssetIndex.ts duplicate ethAssetId entries - add missing clearAssets migration 303 for story chain - add missing VITE_FEATURE_STORY + VITE_STORY_NODE_URL to vite-env.d.ts - fix story native IP relatedAssetKey (was pointing to ETH, should be null) - remove storyAssetId from ethAssetId group in both relatedAssetIndex scripts - add WIP (0x1514...0000) to WRAPPED_NATIVE_CONTRACT_BY_CHAIN_ID for story - regenerate story assets with correct relatedAssetKey=null for native IP Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- restore blank lines removed from utils.ts, baseAssets.ts, coingecko.test.ts, vite-env.d.ts - fix story/zkSyncEra/blast/hemi/sonic blocks merged into one line in utils.ts parseData - story tx status uses generic default handler in useSendActionSubscriber (is second-class evm) - rm story.ts util (redundant - no individual handler needed) - revert comment change in generateChainRelatedAssetIndex.ts - rm duplicate expect() calls in coingecko.test.ts (merge artifacts) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
useSendActionSubscriber had all individual evm chain cases from a bad merge - develop already collapsed them all to default handler, restore to match develop exactly (story uses default, no diff needed there) coingecko.test.ts: two more blank lines restored Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gomesalexandre
left a comment
There was a problem hiding this comment.
Ditto will test this in a few PRs alongside others, diff looking gucci though!
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts (1)
87-96:⚠️ Potential issue | 🟡 MinorComment is missing Sonic — update to "Linea + Mantle + Sonic".
The updated comment on line 87 reads
// Native stablecoins on Linea + Mantle …but both the USDC group (lines 88–92) and the USDT group (lines 93–97) already contain a Sonic entry (eip155:146), making the comment incomplete.📝 Proposed fix
- // Native stablecoins on Linea + Mantle - CoinGecko doesn't tag these as bridged categories + // Native stablecoins on Linea + Mantle + Sonic - CoinGecko doesn't tag these as bridged categories🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts` around lines 87 - 96, Update the inline comment above the USDC/USDT mappings to accurately reflect included chains by changing "// Native stablecoins on Linea + Mantle - CoinGecko doesn't tag these as bridged categories" to include Sonic (e.g. "Linea + Mantle + Sonic") so it matches the entries like 'eip155:146/erc20:...' present in the USDC and USDT groups ('eip155:1/erc20:0xa0b8...' and 'eip155:1/erc20:0xdac1...').
🧹 Nitpick comments (2)
.env (1)
330-330: Optional: dotenv-linter ordering warning —VITE_FEATURE_STORYshould precedeVITE_FEATURE_SUNIO_SWAPalphabetically.The linter flags this because
STORYsorts beforeSUNIOacross the file. Not a correctness issue since the file uses semantic sections, but worth addressing if the project enforces this linter rule.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env at line 330, The .env has VITE_FEATURE_STORY placed after VITE_FEATURE_SUNIO_SWAP which triggers dotenv-linter alphabetical-order warnings; move the VITE_FEATURE_STORY entry so its key appears before VITE_FEATURE_SUNIO_SWAP (i.e., reorder the lines involving VITE_FEATURE_STORY and VITE_FEATURE_SUNIO_SWAP) to satisfy the linter while keeping the same value..env.development (1)
121-121: Optional: dotenv-linter ordering warning —VITE_FEATURE_STORYshould precedeVITE_FEATURE_WC_DIRECT_CONNECTIONalphabetically.Both keys are in the same feature-flags section here (unlike the
.envcase), so the ordering violation is more straightforward.STORYsorts beforeWC_DIRECT_CONNECTION.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env.development at line 121, Reorder the two environment keys in .env.development so they follow dotenv-linter alphabetical rules: move VITE_FEATURE_STORY to appear before VITE_FEATURE_WC_DIRECT_CONNECTION; update the feature-flags section so the lines read with VITE_FEATURE_STORY first and then VITE_FEATURE_WC_DIRECT_CONNECTION to resolve the ordering warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/caip/src/adapters/coingecko/utils.test.ts`:
- Around line 194-196: The test expectation currently asserts that the CoinGecko
ID for 'eip155:1514/slip44:60' is 'story-2', but the utils bug will change the
mapped ID to 'story'; update the assertion in
packages/caip/src/adapters/coingecko/utils.test.ts to expect 'story' instead of
'story-2' for the key 'eip155:1514/slip44:60' (adjust any related fixtures or
expected maps that reference the old 'story-2' value).
In `@packages/caip/src/adapters/coingecko/utils.ts`:
- Line 625: Replace the incorrect CoinGecko ID 'story-2' with the correct
'story' in the mapping entry where the keys are [storyChainId] and
[storyAssetId] (the object currently written as [storyChainId]: {
[storyAssetId]: 'story-2' }) and update the corresponding unit test assertion in
utils.test.ts that expects 'story-2' to instead expect 'story'; ensure both the
mapping and the test reference the CoinGecko coin ID 'story' so market data will
be sourced for Story Protocol's native IP token.
In `@packages/chain-adapters/src/evm/story/StoryChainAdapter.ts`:
- Around line 18-20: Add a defensive type-guard counterpart to the existing
isStoryChainAdapter: implement a function named isStoryChainAdapter that accepts
unknown | null | undefined, first checks for null/undefined and that the value
is an object with a callable getType, then returns (adapter as
ChainAdapter).getType() === KnownChainIds.StoryMainnet; place this in the
utils/story module (the defensive utility layer) so callers can safely pass
nullable values without throwing. Ensure the function signature is exported and
uses the same type predicate "adapter is ChainAdapter" as the original.
---
Outside diff comments:
In
`@scripts/generateAssetData/generateRelatedAssetIndex/generateChainRelatedAssetIndex.ts`:
- Around line 87-96: Update the inline comment above the USDC/USDT mappings to
accurately reflect included chains by changing "// Native stablecoins on Linea +
Mantle - CoinGecko doesn't tag these as bridged categories" to include Sonic
(e.g. "Linea + Mantle + Sonic") so it matches the entries like
'eip155:146/erc20:...' present in the USDC and USDT groups
('eip155:1/erc20:0xa0b8...' and 'eip155:1/erc20:0xdac1...').
---
Nitpick comments:
In @.env:
- Line 330: The .env has VITE_FEATURE_STORY placed after VITE_FEATURE_SUNIO_SWAP
which triggers dotenv-linter alphabetical-order warnings; move the
VITE_FEATURE_STORY entry so its key appears before VITE_FEATURE_SUNIO_SWAP
(i.e., reorder the lines involving VITE_FEATURE_STORY and
VITE_FEATURE_SUNIO_SWAP) to satisfy the linter while keeping the same value.
In @.env.development:
- Line 121: Reorder the two environment keys in .env.development so they follow
dotenv-linter alphabetical rules: move VITE_FEATURE_STORY to appear before
VITE_FEATURE_WC_DIRECT_CONNECTION; update the feature-flags section so the lines
read with VITE_FEATURE_STORY first and then VITE_FEATURE_WC_DIRECT_CONNECTION to
resolve the ordering warning.
| 'eip155:1514': { | ||
| 'eip155:1514/slip44:60': 'story-2', | ||
| }, |
There was a problem hiding this comment.
Test expectation will need updating once the CoinGecko ID bug in utils.ts is fixed.
See the comment on packages/caip/src/adapters/coingecko/utils.ts — the coin ID 'story-2' is incorrect. Once that is corrected to 'story', this expectation should become:
🐛 Proposed fix
- 'eip155:1514': {
- 'eip155:1514/slip44:60': 'story-2',
- },
+ 'eip155:1514': {
+ 'eip155:1514/slip44:60': 'story',
+ },📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'eip155:1514': { | |
| 'eip155:1514/slip44:60': 'story-2', | |
| }, | |
| 'eip155:1514': { | |
| 'eip155:1514/slip44:60': 'story', | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/caip/src/adapters/coingecko/utils.test.ts` around lines 194 - 196,
The test expectation currently asserts that the CoinGecko ID for
'eip155:1514/slip44:60' is 'story-2', but the utils bug will change the mapped
ID to 'story'; update the assertion in
packages/caip/src/adapters/coingecko/utils.test.ts to expect 'story' instead of
'story-2' for the key 'eip155:1514/slip44:60' (adjust any related fixtures or
expected maps that reference the old 'story-2' value).
| [berachainChainId]: { [berachainAssetId]: 'berachain-bera' }, | ||
| [cronosChainId]: { [cronosAssetId]: 'crypto-com-chain' }, | ||
| [katanaChainId]: { [katanaAssetId]: 'katana' }, | ||
| [storyChainId]: { [storyAssetId]: 'story-2' }, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Story Protocol native IP token CoinGecko coin ID
💡 Result:
Story Protocol’s native IP token (“Story”, ticker IP) has CoinGecko coin ID: story. [1]
🏁 Script executed:
# Check the actual line 625 in the utils.ts file
sed -n '620,630p' packages/caip/src/adapters/coingecko/utils.tsRepository: shapeshift/web
Length of output: 696
🏁 Script executed:
# Check for all references to story in the coingecko utils file
rg -n "story" packages/caip/src/adapters/coingecko/utils.ts -iRepository: shapeshift/web
Length of output: 413
🏁 Script executed:
# Check the test file for references to story
rg -n "story" packages/caip/src/adapters/coingecko/utils.test.ts -i -A 2 -B 2Repository: shapeshift/web
Length of output: 194
Wrong CoinGecko coin ID for Story Protocol's native IP asset — will source market data from incorrect token.
'story-2' is a defunct charity token on BNB Chain with no active trading. Story Protocol's native IP token has CoinGecko coin ID 'story'.
Update line 625 in utils.ts and line 195 in utils.test.ts:
Proposed fixes
- [storyChainId]: { [storyAssetId]: 'story-2' },
+ [storyChainId]: { [storyAssetId]: 'story' },- 'eip155:1514/slip44:60': 'story-2',
+ 'eip155:1514/slip44:60': 'story',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/caip/src/adapters/coingecko/utils.ts` at line 625, Replace the
incorrect CoinGecko ID 'story-2' with the correct 'story' in the mapping entry
where the keys are [storyChainId] and [storyAssetId] (the object currently
written as [storyChainId]: { [storyAssetId]: 'story-2' }) and update the
corresponding unit test assertion in utils.test.ts that expects 'story-2' to
instead expect 'story'; ensure both the mapping and the test reference the
CoinGecko coin ID 'story' so market data will be sourced for Story Protocol's
native IP token.
| export const isStoryChainAdapter = (adapter: unknown): adapter is ChainAdapter => { | ||
| return (adapter as ChainAdapter).getType() === KnownChainIds.StoryMainnet | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if the utility-layer counterpart with defensive checks exists
fd "story.ts" src/lib/utils/
rg -n "isStoryChainAdapter" src/lib/Repository: shapeshift/web
Length of output: 40
Add defensive isStoryChainAdapter to src/lib/utils/story.ts.
The defensive counterpart with null/undefined checks is missing from src/lib/utils/story.ts. Per the established pattern across second-class EVM adapters (mantle, katana, unichain, soneium, and 10+ others), the type guard must exist in both locations: in the adapter file (non-defensive, as written) and in the utility layer (with defensive checks). Create src/lib/utils/story.ts with a defensive version of isStoryChainAdapter that safely handles null/undefined inputs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/chain-adapters/src/evm/story/StoryChainAdapter.ts` around lines 18 -
20, Add a defensive type-guard counterpart to the existing isStoryChainAdapter:
implement a function named isStoryChainAdapter that accepts unknown | null |
undefined, first checks for null/undefined and that the value is an object with
a callable getType, then returns (adapter as ChainAdapter).getType() ===
KnownChainIds.StoryMainnet; place this in the utils/story module (the defensive
utility layer) so callers can safely pass nullable values without throwing.
Ensure the function signature is exported and uses the same type predicate
"adapter is ChainAdapter" as the original.
Description
Add support for Story (EVM chain, chainId 1514) as a second-class citizen. Story is an EVM-compatible Layer 1 blockchain for intellectual property, with IP as its native token.
This is PR 12 of 17 in a sequential chain integration series. These PRs must be reviewed and merged in order, as each builds on the previous one (stacked branches).
PR merge order:
Implements: CAIP constants, chain adapter (SecondClassEvmAdapter), plugin, feature flag, Relay swapper mapping, HDWallet support flags, CSP headers, asset generation script, viem/ethers client configuration, CoinGecko price feed integration, Zerion portfolio tracking, and all required shared-file entries.
Issue (if applicable)
Part of #11902
Risk
Low - All changes are behind the
Storyfeature flag (VITE_FEATURE_STORY), disabled by default in production.No new on-chain transactions or contract interactions. Standard EVM chain support using existing SecondClassEvmAdapter pattern.
Testing
Engineering
VITE_FEATURE_STORY=truein.env.developmentyarn devyarn lint— passes with 0 errorsyarn type-check— verify no new type errorsOperations
Screenshots (if applicable)
N/A - Behind feature flag, no visual changes until enabled.
Summary by CodeRabbit
Release Notes
New Features
Configuration